"""
Description: General utilities for the package.

Author: Jin Liu
Date: 2024/03/14
"""
import sys
import time
import threading
from typing import Callable

__all__ = [
    'Filter', 
    'Timer', 
    'CircularBuffer', 
    'accurate_sleep'
]


class Filter(object):
    """
    Filter class.
    """
    def __init__(self, data=None, alpha=0.2):
        """Initialization.

        Parameters:
        - data : num or array, optional, default: None, initial data;
        - alpha : float, optional, default: 0.2, the filter coefficient.
        """
        self._data = data
        self._alpha = alpha

    def update(self, new_data):
        """Update the data.

        Parameters:
        - new_data : num or array, the new data.
        """
        if self._data is None:
            self._data = new_data
        self._data = (1 - self._alpha) * self._data + self._alpha * new_data

    @property
    def data(self):
        if self._data is None:
            return 0
        return self._data

    @data.setter
    def data(self, new_data):
        if self._data is None or self._alpha == 1:
            self._data = new_data
        self._data = (1 - self._alpha) * self._data + self._alpha * new_data


# accurate sleep
if sys.platform == "win32":
    import ctypes
    from ctypes.wintypes import LARGE_INTEGER
    kernel32 = ctypes.windll.kernel32
    INFINITE = 0xFFFFFFFF
    CREATE_WAITABLE_TIMER_HIGH_RESOLUTION = 0x00000002

    def accurate_sleep(seconds):
        """
        accurate sleep function for Windows.

        Parameters:
        - seconds : float
        """
        handle = kernel32.CreateWaitableTimerExW(
            None, None, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, 0x1F0003
        )
        res = kernel32.SetWaitableTimer(
            handle,
            ctypes.byref(LARGE_INTEGER(int(seconds * -10000000))),
            0, None, None, 0,
        )
        res = kernel32.WaitForSingleObject(handle, INFINITE)
        kernel32.CancelWaitableTimer(handle)
else:
    accurate_sleep = time.sleep


class CircularBuffer:
    def __init__(self, size):
        self.size = size # 缓冲区大小
        self.buffer =[None] * size
        self.ptr = -1   #指针

    def put(self, item):
        """将元素加入到尾指针处"""
        next = (self.ptr + 1) % self.size
        self.buffer[next]= item
        self.ptr = next

    def get(self):
        """返回尾指针所指元素"""
        if self.ptr != -1:
            return self.buffer[self.ptr]
        else:
            return None
        
    def clear(self):
        """清空缓冲区"""
        self.ptr = -1
        self.buffer =[None] * self.size


class Timer:
    def __init__(
        self, 
        frequency,
        callback: Callable,
        start=False, 
    ):
        self._sleep_time_s = 1 / frequency
        self._is_running = threading.Event()
        self._callback = callback
        self._thread = None

        if start:
            self.start()

    def run(self):
        self._is_running.set()
        while self._is_running.is_set():
            t0 = time.monotonic()  # 使用单调时钟避免系统时间跳变

            self._callback()
            
            elapsed_time = time.monotonic() - t0
            if self._sleep_time_s > elapsed_time:
                accurate_sleep(self._sleep_time_s - elapsed_time)

    def start(self):
        if self._is_running.is_set():
            return
        
        self._thread = threading.Thread(target=self.run, daemon=True)
        self._thread.start()

    def stop(self):
        self._is_running.clear()
        if self._thread is not None:
            self._thread.join()
            self._thread = None
    
    def alive(self):
        return self._thread.is_alive()

